﻿using System;
using System.Collections.Generic;
using System.Text;

namespace BeatSaberModManager.Dependencies
{
    /* * * * *
* A simple JSON Parser / builder
* ------------------------------
* 
* It mainly has been written as a simple JSON parser. It can build a JSON string
* from the node-tree, or generate a node tree from any valid JSON string.
* 
* If you want to use compression when saving to file / stream / B64 you have to include
* SharpZipLib ( http://www.icsharpcode.net/opensource/sharpziplib/ ) in your project and
* define "USE_SharpZipLib" at the top of the file
* 
* Written by Bunny83 
* 2012-06-09
* 
* [2012-06-09 First Version]
* - provides strongly typed node classes and lists / dictionaries
* - provides easy access to class members / array items / data values
* - the parser now properly identifies types. So generating JSON with this framework should work.
* - only double quotes (") are used for quoting strings.
* - provides "casting" properties to easily convert to / from those types:
*   int / float / double / bool
* - provides a common interface for each node so no explicit casting is required.
* - the parser tries to avoid errors, but if malformed JSON is parsed the result is more or less undefined
* - It can serialize/deserialize a node tree into/from an experimental compact binary format. It might
*   be handy if you want to store things in a file and don't want it to be easily modifiable
* 
* [2012-12-17 Update]
* - Added internal JSONLazyCreator class which simplifies the construction of a JSON tree
*   Now you can simple reference any item that doesn't exist yet and it will return a JSONLazyCreator
*   The class determines the required type by it's further use, creates the type and removes itself.
* - Added binary serialization / deserialization.
* - Added support for BZip2 zipped binary format. Requires the SharpZipLib ( http://www.icsharpcode.net/opensource/sharpziplib/ )
*   The usage of the SharpZipLib library can be disabled by removing or commenting out the USE_SharpZipLib define at the top
* - The serializer uses different types when it comes to store the values. Since my data values
*   are all of type string, the serializer will "try" which format fits best. The order is: int, float, double, bool, string.
*   It's not the most efficient way but for a moderate amount of data it should work on all platforms.
* 
* [2017-03-08 Update]
* - Optimised parsing by using a StringBuilder for token. This prevents performance issues when large
*   string data fields are contained in the json data.
* - Finally refactored the badly named JSONClass into JSONObject.
* - Replaced the old JSONData class by distict typed classes ( JSONString, JSONNumber, JSONBool, JSONNull ) this
*   allows to propertly convert the node tree back to json without type information loss. The actual value
*   parsing now happens at parsing time and not when you actually access one of the casting properties.
* 
* [2017-04-11 Update]
* - Fixed parsing bug where empty string values have been ignored.
* - Optimised "ToString" by using a StringBuilder internally. This should heavily improve performance for large files
* - Changed the overload of "ToString(string aIndent)" to "ToString(int aIndent)"
* 
* [2017-11-29 Update]
* - Removed the IEnumerator implementations on JSONArray & JSONObject and replaced it with a common
*   struct Enumerator in JSONNode that should avoid garbage generation. The enumerator always works
*   on KeyValuePair<string, JSONNode>, even for JSONArray.
* - Added two wrapper Enumerators that allows for easy key or value enumeration. A JSONNode now has
*   a "Keys" and a "Values" enumerable property. Those are also struct enumerators / enumerables
* - A KeyValuePair<string, JSONNode> can now be implicitly converted into a JSONNode. This allows
*   a foreach loop over a JSONNode to directly access the values only. Since KeyValuePair as well as
*   all the Enumerators are structs, no garbage is allocated.
* - To add Linq support another "LinqEnumerator" is available through the "Linq" property. This
*   enumerator does implement the generic IEnumerable interface so most Linq extensions can be used
*   on this enumerable object. This one does allocate memory as it's a wrapper class.
* - The Escape method now escapes all control characters (# < 32) in strings as uncode characters
*   (\uXXXX) and if the static bool JSONNode.forceASCII is set to true it will also escape all
*   characters # > 127. This might be useful if you require an ASCII output. Though keep in mind
*   when your strings contain many non-ascii characters the strings become much longer (x6) and are
*   no longer human readable.
* - The node types JSONObject and JSONArray now have an "Inline" boolean switch which will default to
*   false. It can be used to serialize this element inline even you serialize with an indented format
*   This is useful for arrays containing numbers so it doesn't place every number on a new line
* - Extracted the binary serialization code into a seperate extension file. All classes are now declared
*   as "partial" so an extension file can even add a new virtual or abstract method / interface to
*   JSONNode and override it in the concrete type classes. It's of course a hacky approach which is
*   generally not recommended, but i wanted to keep everything tightly packed.
* - Added a static CreateOrGet method to the JSONNull class. Since this class is immutable it could
*   be reused without major problems. If you have a lot null fields in your data it will help reduce
*   the memory / garbage overhead. I also added a static setting (reuseSameInstance) to JSONNull
*   (default is true) which will change the behaviour of "CreateOrGet". If you set this to false
*   CreateOrGet will not reuse the cached instance but instead create a new JSONNull instance each time.
*   I made the JSONNull constructor private so if you need to create an instance manually use
*   JSONNull.CreateOrGet()
* 
* [2018-01-09 Update]
* - Changed all double.TryParse and double.ToString uses to use the invariant culture to avoid problems
*   on systems with a culture that uses a comma as decimal point.
* 
* [2018-01-26 Update]
* - Added AsLong. Note that a JSONNumber is stored as double and can't represent all long values. However
*   storing it as string would work.
* - Added static setting "JSONNode.longAsString" which controls the default type that is used by the
*   LazyCreator when using AsLong
* 
* [2018-04-25 Update]
*  - Added support for parsing single values (JSONBool, JSONString, JSONNumber, JSONNull) as top level value.
* 
* The MIT License (MIT)
* 
* Copyright (c) 2012-2017 Markus Göbel (Bunny83)
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* 
* * * * */
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Text;

    namespace SimpleJSON
    {
        public enum JSONNodeType
        {
            Array = 1,
            Object = 2,
            String = 3,
            Number = 4,
            NullValue = 5,
            Boolean = 6,
            None = 7,
            Custom = 0xFF,
        }
        public enum JSONTextMode
        {
            Compact,
            Indent
        }

        public abstract partial class JSONNode
        {
            #region Enumerators
            public struct Enumerator
            {
                private enum Type { None, Array, Object }
                private Type type;
                private Dictionary<string, JSONNode>.Enumerator m_Object;
                private List<JSONNode>.Enumerator m_Array;
                public bool IsValid { get { return type != Type.None; } }
                public Enumerator(List<JSONNode>.Enumerator aArrayEnum)
                {
                    type = Type.Array;
                    m_Object = default(Dictionary<string, JSONNode>.Enumerator);
                    m_Array = aArrayEnum;
                }
                public Enumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum)
                {
                    type = Type.Object;
                    m_Object = aDictEnum;
                    m_Array = default(List<JSONNode>.Enumerator);
                }
                public KeyValuePair<string, JSONNode> Current
                {
                    get
                    {
                        if (type == Type.Array)
                            return new KeyValuePair<string, JSONNode>(string.Empty, m_Array.Current);
                        else if (type == Type.Object)
                            return m_Object.Current;
                        return new KeyValuePair<string, JSONNode>(string.Empty, null);
                    }
                }
                public bool MoveNext()
                {
                    if (type == Type.Array)
                        return m_Array.MoveNext();
                    else if (type == Type.Object)
                        return m_Object.MoveNext();
                    return false;
                }
            }
            public struct ValueEnumerator
            {
                private Enumerator m_Enumerator;
                public ValueEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { }
                public ValueEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { }
                public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; }
                public JSONNode Current { get { return m_Enumerator.Current.Value; } }
                public bool MoveNext() { return m_Enumerator.MoveNext(); }
                public ValueEnumerator GetEnumerator() { return this; }
            }
            public struct KeyEnumerator
            {
                private Enumerator m_Enumerator;
                public KeyEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { }
                public KeyEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { }
                public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; }
                public JSONNode Current { get { return m_Enumerator.Current.Key; } }
                public bool MoveNext() { return m_Enumerator.MoveNext(); }
                public KeyEnumerator GetEnumerator() { return this; }
            }

            public class LinqEnumerator : IEnumerator<KeyValuePair<string, JSONNode>>, IEnumerable<KeyValuePair<string, JSONNode>>
            {
                private JSONNode m_Node;
                private Enumerator m_Enumerator;
                internal LinqEnumerator(JSONNode aNode)
                {
                    m_Node = aNode;
                    if (m_Node != null)
                        m_Enumerator = m_Node.GetEnumerator();
                }
                public KeyValuePair<string, JSONNode> Current { get { return m_Enumerator.Current; } }
                object IEnumerator.Current { get { return m_Enumerator.Current; } }
                public bool MoveNext() { return m_Enumerator.MoveNext(); }

                public void Dispose()
                {
                    m_Node = null;
                    m_Enumerator = new Enumerator();
                }

                public IEnumerator<KeyValuePair<string, JSONNode>> GetEnumerator()
                {
                    return new LinqEnumerator(m_Node);
                }

                public void Reset()
                {
                    if (m_Node != null)
                        m_Enumerator = m_Node.GetEnumerator();
                }

                IEnumerator IEnumerable.GetEnumerator()
                {
                    return new LinqEnumerator(m_Node);
                }
            }

            #endregion Enumerators

            #region common interface

            public static bool forceASCII = false; // Use Unicode by default
            public static bool longAsString = false; // lazy creator creates a JSONString instead of JSONNumber

            public abstract JSONNodeType Tag { get; }

            public virtual JSONNode this[int aIndex] { get { return null; } set { } }

            public virtual JSONNode this[string aKey] { get { return null; } set { } }

            public virtual string Value { get { return ""; } set { } }

            public virtual int Count { get { return 0; } }

            public virtual bool IsNumber { get { return false; } }
            public virtual bool IsString { get { return false; } }
            public virtual bool IsBoolean { get { return false; } }
            public virtual bool IsNull { get { return false; } }
            public virtual bool IsArray { get { return false; } }
            public virtual bool IsObject { get { return false; } }

            public virtual bool Inline { get { return false; } set { } }

            public virtual void Add(string aKey, JSONNode aItem)
            {
            }
            public virtual void Add(JSONNode aItem)
            {
                Add("", aItem);
            }

            public virtual JSONNode Remove(string aKey)
            {
                return null;
            }

            public virtual JSONNode Remove(int aIndex)
            {
                return null;
            }

            public virtual JSONNode Remove(JSONNode aNode)
            {
                return aNode;
            }

            public virtual IEnumerable<JSONNode> Children
            {
                get
                {
                    yield break;
                }
            }

            public IEnumerable<JSONNode> DeepChildren
            {
                get
                {
                    foreach (var C in Children)
                        foreach (var D in C.DeepChildren)
                            yield return D;
                }
            }

            public override string ToString()
            {
                StringBuilder sb = new StringBuilder();
                WriteToStringBuilder(sb, 0, 0, JSONTextMode.Compact);
                return sb.ToString();
            }

            public virtual string ToString(int aIndent)
            {
                StringBuilder sb = new StringBuilder();
                WriteToStringBuilder(sb, 0, aIndent, JSONTextMode.Indent);
                return sb.ToString();
            }
            internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode);

            public abstract Enumerator GetEnumerator();
            public IEnumerable<KeyValuePair<string, JSONNode>> Linq { get { return new LinqEnumerator(this); } }
            public KeyEnumerator Keys { get { return new KeyEnumerator(GetEnumerator()); } }
            public ValueEnumerator Values { get { return new ValueEnumerator(GetEnumerator()); } }

            #endregion common interface

            #region typecasting properties


            public virtual double AsDouble
            {
                get
                {
                    double v = 0.0;
                    if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out v))
                        return v;
                    return 0.0;
                }
                set
                {
                    Value = value.ToString(CultureInfo.InvariantCulture);
                }
            }

            public virtual int AsInt
            {
                get { return (int)AsDouble; }
                set { AsDouble = value; }
            }

            public virtual float AsFloat
            {
                get { return (float)AsDouble; }
                set { AsDouble = value; }
            }

            public virtual bool AsBool
            {
                get
                {
                    bool v = false;
                    if (bool.TryParse(Value, out v))
                        return v;
                    return !string.IsNullOrEmpty(Value);
                }
                set
                {
                    Value = (value) ? "true" : "false";
                }
            }

            public virtual long AsLong
            {
                get
                {
                    long val = 0;
                    if (long.TryParse(Value, out val))
                        return val;
                    return 0L;
                }
                set
                {
                    Value = value.ToString();
                }
            }

            public virtual JSONArray AsArray
            {
                get
                {
                    return this as JSONArray;
                }
            }

            public virtual JSONObject AsObject
            {
                get
                {
                    return this as JSONObject;
                }
            }


            #endregion typecasting properties

            #region operators

            public static implicit operator JSONNode(string s)
            {
                return new JSONString(s);
            }
            public static implicit operator string(JSONNode d)
            {
                return (d == null) ? null : d.Value;
            }

            public static implicit operator JSONNode(double n)
            {
                return new JSONNumber(n);
            }
            public static implicit operator double(JSONNode d)
            {
                return (d == null) ? 0 : d.AsDouble;
            }

            public static implicit operator JSONNode(float n)
            {
                return new JSONNumber(n);
            }
            public static implicit operator float(JSONNode d)
            {
                return (d == null) ? 0 : d.AsFloat;
            }

            public static implicit operator JSONNode(int n)
            {
                return new JSONNumber(n);
            }
            public static implicit operator int(JSONNode d)
            {
                return (d == null) ? 0 : d.AsInt;
            }

            public static implicit operator JSONNode(long n)
            {
                if (longAsString)
                    return new JSONString(n.ToString());
                return new JSONNumber(n);
            }
            public static implicit operator long(JSONNode d)
            {
                return (d == null) ? 0L : d.AsLong;
            }

            public static implicit operator JSONNode(bool b)
            {
                return new JSONBool(b);
            }
            public static implicit operator bool(JSONNode d)
            {
                return (d == null) ? false : d.AsBool;
            }

            public static implicit operator JSONNode(KeyValuePair<string, JSONNode> aKeyValue)
            {
                return aKeyValue.Value;
            }

            public static bool operator ==(JSONNode a, object b)
            {
                if (ReferenceEquals(a, b))
                    return true;
                bool aIsNull = a is JSONNull || ReferenceEquals(a, null) || a is JSONLazyCreator;
                bool bIsNull = b is JSONNull || ReferenceEquals(b, null) || b is JSONLazyCreator;
                if (aIsNull && bIsNull)
                    return true;
                return !aIsNull && a.Equals(b);
            }

            public static bool operator !=(JSONNode a, object b)
            {
                return !(a == b);
            }

            public override bool Equals(object obj)
            {
                return ReferenceEquals(this, obj);
            }

            public override int GetHashCode()
            {
                return base.GetHashCode();
            }

            #endregion operators

            [ThreadStatic]
            private static StringBuilder m_EscapeBuilder;
            internal static StringBuilder EscapeBuilder
            {
                get
                {
                    if (m_EscapeBuilder == null)
                        m_EscapeBuilder = new StringBuilder();
                    return m_EscapeBuilder;
                }
            }
            internal static string Escape(string aText)
            {
                var sb = EscapeBuilder;
                sb.Length = 0;
                if (sb.Capacity < aText.Length + aText.Length / 10)
                    sb.Capacity = aText.Length + aText.Length / 10;
                foreach (char c in aText)
                {
                    switch (c)
                    {
                        case '\\':
                            sb.Append("\\\\");
                            break;
                        case '\"':
                            sb.Append("\\\"");
                            break;
                        case '\n':
                            sb.Append("\\n");
                            break;
                        case '\r':
                            sb.Append("\\r");
                            break;
                        case '\t':
                            sb.Append("\\t");
                            break;
                        case '\b':
                            sb.Append("\\b");
                            break;
                        case '\f':
                            sb.Append("\\f");
                            break;
                        default:
                            if (c < ' ' || (forceASCII && c > 127))
                            {
                                ushort val = c;
                                sb.Append("\\u").Append(val.ToString("X4"));
                            }
                            else
                                sb.Append(c);
                            break;
                    }
                }
                string result = sb.ToString();
                sb.Length = 0;
                return result;
            }

            private static JSONNode ParseElement(string token, bool quoted)
            {
                if (quoted)
                    return token;
                string tmp = token.ToLower();
                if (tmp == "false" || tmp == "true")
                    return tmp == "true";
                if (tmp == "null")
                    return JSONNull.CreateOrGet();
                double val;
                if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out val))
                    return val;
                else
                    return token;
            }

            public static JSONNode Parse(string aJSON)
            {
                Stack<JSONNode> stack = new Stack<JSONNode>();
                JSONNode ctx = null;
                int i = 0;
                StringBuilder Token = new StringBuilder();
                string TokenName = "";
                bool QuoteMode = false;
                bool TokenIsQuoted = false;
                while (i < aJSON.Length)
                {
                    switch (aJSON[i])
                    {
                        case '{':
                            if (QuoteMode)
                            {
                                Token.Append(aJSON[i]);
                                break;
                            }
                            stack.Push(new JSONObject());
                            if (ctx != null)
                            {
                                ctx.Add(TokenName, stack.Peek());
                            }
                            TokenName = "";
                            Token.Length = 0;
                            ctx = stack.Peek();
                            break;

                        case '[':
                            if (QuoteMode)
                            {
                                Token.Append(aJSON[i]);
                                break;
                            }

                            stack.Push(new JSONArray());
                            if (ctx != null)
                            {
                                ctx.Add(TokenName, stack.Peek());
                            }
                            TokenName = "";
                            Token.Length = 0;
                            ctx = stack.Peek();
                            break;

                        case '}':
                        case ']':
                            if (QuoteMode)
                            {

                                Token.Append(aJSON[i]);
                                break;
                            }
                            if (stack.Count == 0)
                                throw new Exception("JSON Parse: Too many closing brackets");

                            stack.Pop();
                            if (Token.Length > 0 || TokenIsQuoted)
                                ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted));
                            TokenIsQuoted = false;
                            TokenName = "";
                            Token.Length = 0;
                            if (stack.Count > 0)
                                ctx = stack.Peek();
                            break;

                        case ':':
                            if (QuoteMode)
                            {
                                Token.Append(aJSON[i]);
                                break;
                            }
                            TokenName = Token.ToString();
                            Token.Length = 0;
                            TokenIsQuoted = false;
                            break;

                        case '"':
                            QuoteMode ^= true;
                            TokenIsQuoted |= QuoteMode;
                            break;

                        case ',':
                            if (QuoteMode)
                            {
                                Token.Append(aJSON[i]);
                                break;
                            }
                            if (Token.Length > 0 || TokenIsQuoted)
                                ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted));
                            TokenIsQuoted = false;
                            TokenName = "";
                            Token.Length = 0;
                            TokenIsQuoted = false;
                            break;

                        case '\r':
                        case '\n':
                            break;

                        case ' ':
                        case '\t':
                            if (QuoteMode)
                                Token.Append(aJSON[i]);
                            break;

                        case '\\':
                            ++i;
                            if (QuoteMode)
                            {
                                char C = aJSON[i];
                                switch (C)
                                {
                                    case 't':
                                        Token.Append('\t');
                                        break;
                                    case 'r':
                                        Token.Append('\r');
                                        break;
                                    case 'n':
                                        Token.Append('\n');
                                        break;
                                    case 'b':
                                        Token.Append('\b');
                                        break;
                                    case 'f':
                                        Token.Append('\f');
                                        break;
                                    case 'u':
                                        {
                                            string s = aJSON.Substring(i + 1, 4);
                                            Token.Append((char)int.Parse(
                                                s,
                                                System.Globalization.NumberStyles.AllowHexSpecifier));
                                            i += 4;
                                            break;
                                        }
                                    default:
                                        Token.Append(C);
                                        break;
                                }
                            }
                            break;

                        default:
                            Token.Append(aJSON[i]);
                            break;
                    }
                    ++i;
                }
                if (QuoteMode)
                {
                    throw new Exception("JSON Parse: Quotation marks seems to be messed up.");
                }
                if (ctx == null)
                    return ParseElement(Token.ToString(), TokenIsQuoted);
                return ctx;
            }

        }
        // End of JSONNode

        public partial class JSONArray : JSONNode
        {
            private List<JSONNode> m_List = new List<JSONNode>();
            private bool inline = false;
            public override bool Inline
            {
                get { return inline; }
                set { inline = value; }
            }

            public override JSONNodeType Tag { get { return JSONNodeType.Array; } }
            public override bool IsArray { get { return true; } }
            public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); }

            public override JSONNode this[int aIndex]
            {
                get
                {
                    if (aIndex < 0 || aIndex >= m_List.Count)
                        return new JSONLazyCreator(this);
                    return m_List[aIndex];
                }
                set
                {
                    if (value == null)
                        value = JSONNull.CreateOrGet();
                    if (aIndex < 0 || aIndex >= m_List.Count)
                        m_List.Add(value);
                    else
                        m_List[aIndex] = value;
                }
            }

            public override JSONNode this[string aKey]
            {
                get { return new JSONLazyCreator(this); }
                set
                {
                    if (value == null)
                        value = JSONNull.CreateOrGet();
                    m_List.Add(value);
                }
            }

            public override int Count
            {
                get { return m_List.Count; }
            }

            public override void Add(string aKey, JSONNode aItem)
            {
                if (aItem == null)
                    aItem = JSONNull.CreateOrGet();
                m_List.Add(aItem);
            }

            public override JSONNode Remove(int aIndex)
            {
                if (aIndex < 0 || aIndex >= m_List.Count)
                    return null;
                JSONNode tmp = m_List[aIndex];
                m_List.RemoveAt(aIndex);
                return tmp;
            }

            public override JSONNode Remove(JSONNode aNode)
            {
                m_List.Remove(aNode);
                return aNode;
            }

            public override IEnumerable<JSONNode> Children
            {
                get
                {
                    foreach (JSONNode N in m_List)
                        yield return N;
                }
            }


            internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
            {
                aSB.Append('[');
                int count = m_List.Count;
                if (inline)
                    aMode = JSONTextMode.Compact;
                for (int i = 0; i < count; i++)
                {
                    if (i > 0)
                        aSB.Append(',');
                    if (aMode == JSONTextMode.Indent)
                        aSB.AppendLine();

                    if (aMode == JSONTextMode.Indent)
                        aSB.Append(' ', aIndent + aIndentInc);
                    m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode);
                }
                if (aMode == JSONTextMode.Indent)
                    aSB.AppendLine().Append(' ', aIndent);
                aSB.Append(']');
            }
        }
        // End of JSONArray

        public partial class JSONObject : JSONNode
        {
            private Dictionary<string, JSONNode> m_Dict = new Dictionary<string, JSONNode>();

            private bool inline = false;
            public override bool Inline
            {
                get { return inline; }
                set { inline = value; }
            }

            public override JSONNodeType Tag { get { return JSONNodeType.Object; } }
            public override bool IsObject { get { return true; } }

            public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); }


            public override JSONNode this[string aKey]
            {
                get
                {
                    if (m_Dict.ContainsKey(aKey))
                        return m_Dict[aKey];
                    else
                        return new JSONLazyCreator(this, aKey);
                }
                set
                {
                    if (value == null)
                        value = JSONNull.CreateOrGet();
                    if (m_Dict.ContainsKey(aKey))
                        m_Dict[aKey] = value;
                    else
                        m_Dict.Add(aKey, value);
                }
            }

            public override JSONNode this[int aIndex]
            {
                get
                {
                    if (aIndex < 0 || aIndex >= m_Dict.Count)
                        return null;
                    return m_Dict.ElementAt(aIndex).Value;
                }
                set
                {
                    if (value == null)
                        value = JSONNull.CreateOrGet();
                    if (aIndex < 0 || aIndex >= m_Dict.Count)
                        return;
                    string key = m_Dict.ElementAt(aIndex).Key;
                    m_Dict[key] = value;
                }
            }

            public override int Count
            {
                get { return m_Dict.Count; }
            }

            public override void Add(string aKey, JSONNode aItem)
            {
                if (aItem == null)
                    aItem = JSONNull.CreateOrGet();

                if (!string.IsNullOrEmpty(aKey))
                {
                    if (m_Dict.ContainsKey(aKey))
                        m_Dict[aKey] = aItem;
                    else
                        m_Dict.Add(aKey, aItem);
                }
                else
                    m_Dict.Add(Guid.NewGuid().ToString(), aItem);
            }

            public override JSONNode Remove(string aKey)
            {
                if (!m_Dict.ContainsKey(aKey))
                    return null;
                JSONNode tmp = m_Dict[aKey];
                m_Dict.Remove(aKey);
                return tmp;
            }

            public override JSONNode Remove(int aIndex)
            {
                if (aIndex < 0 || aIndex >= m_Dict.Count)
                    return null;
                var item = m_Dict.ElementAt(aIndex);
                m_Dict.Remove(item.Key);
                return item.Value;
            }

            public override JSONNode Remove(JSONNode aNode)
            {
                try
                {
                    var item = m_Dict.Where(k => k.Value == aNode).First();
                    m_Dict.Remove(item.Key);
                    return aNode;
                }
                catch
                {
                    return null;
                }
            }

            public override IEnumerable<JSONNode> Children
            {
                get
                {
                    foreach (KeyValuePair<string, JSONNode> N in m_Dict)
                        yield return N.Value;
                }
            }

            internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
            {
                aSB.Append('{');
                bool first = true;
                if (inline)
                    aMode = JSONTextMode.Compact;
                foreach (var k in m_Dict)
                {
                    if (!first)
                        aSB.Append(',');
                    first = false;
                    if (aMode == JSONTextMode.Indent)
                        aSB.AppendLine();
                    if (aMode == JSONTextMode.Indent)
                        aSB.Append(' ', aIndent + aIndentInc);
                    aSB.Append('\"').Append(Escape(k.Key)).Append('\"');
                    if (aMode == JSONTextMode.Compact)
                        aSB.Append(':');
                    else
                        aSB.Append(" : ");
                    k.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode);
                }
                if (aMode == JSONTextMode.Indent)
                    aSB.AppendLine().Append(' ', aIndent);
                aSB.Append('}');
            }

        }
        // End of JSONObject

        public partial class JSONString : JSONNode
        {
            private string m_Data;

            public override JSONNodeType Tag { get { return JSONNodeType.String; } }
            public override bool IsString { get { return true; } }

            public override Enumerator GetEnumerator() { return new Enumerator(); }


            public override string Value
            {
                get { return m_Data; }
                set
                {
                    m_Data = value;
                }
            }

            public JSONString(string aData)
            {
                m_Data = aData;
            }

            internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
            {
                aSB.Append('\"').Append(Escape(m_Data)).Append('\"');
            }
            public override bool Equals(object obj)
            {
                if (base.Equals(obj))
                    return true;
                string s = obj as string;
                if (s != null)
                    return m_Data == s;
                JSONString s2 = obj as JSONString;
                if (s2 != null)
                    return m_Data == s2.m_Data;
                return false;
            }
            public override int GetHashCode()
            {
                return m_Data.GetHashCode();
            }
        }
        // End of JSONString

        public partial class JSONNumber : JSONNode
        {
            private double m_Data;

            public override JSONNodeType Tag { get { return JSONNodeType.Number; } }
            public override bool IsNumber { get { return true; } }
            public override Enumerator GetEnumerator() { return new Enumerator(); }

            public override string Value
            {
                get { return m_Data.ToString(CultureInfo.InvariantCulture); }
                set
                {
                    double v;
                    if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out v))
                        m_Data = v;
                }
            }

            public override double AsDouble
            {
                get { return m_Data; }
                set { m_Data = value; }
            }
            public override long AsLong
            {
                get { return (long)m_Data; }
                set { m_Data = value; }
            }

            public JSONNumber(double aData)
            {
                m_Data = aData;
            }

            public JSONNumber(string aData)
            {
                Value = aData;
            }

            internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
            {
                aSB.Append(Value);
            }
            private static bool IsNumeric(object value)
            {
                return value is int || value is uint
                    || value is float || value is double
                    || value is decimal
                    || value is long || value is ulong
                    || value is short || value is ushort
                    || value is sbyte || value is byte;
            }
            public override bool Equals(object obj)
            {
                if (obj == null)
                    return false;
                if (base.Equals(obj))
                    return true;
                JSONNumber s2 = obj as JSONNumber;
                if (s2 != null)
                    return m_Data == s2.m_Data;
                if (IsNumeric(obj))
                    return Convert.ToDouble(obj) == m_Data;
                return false;
            }
            public override int GetHashCode()
            {
                return m_Data.GetHashCode();
            }
        }
        // End of JSONNumber

        public partial class JSONBool : JSONNode
        {
            private bool m_Data;

            public override JSONNodeType Tag { get { return JSONNodeType.Boolean; } }
            public override bool IsBoolean { get { return true; } }
            public override Enumerator GetEnumerator() { return new Enumerator(); }

            public override string Value
            {
                get { return m_Data.ToString(); }
                set
                {
                    bool v;
                    if (bool.TryParse(value, out v))
                        m_Data = v;
                }
            }
            public override bool AsBool
            {
                get { return m_Data; }
                set { m_Data = value; }
            }

            public JSONBool(bool aData)
            {
                m_Data = aData;
            }

            public JSONBool(string aData)
            {
                Value = aData;
            }

            internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
            {
                aSB.Append((m_Data) ? "true" : "false");
            }
            public override bool Equals(object obj)
            {
                if (obj == null)
                    return false;
                if (obj is bool)
                    return m_Data == (bool)obj;
                return false;
            }
            public override int GetHashCode()
            {
                return m_Data.GetHashCode();
            }
        }
        // End of JSONBool

        public partial class JSONNull : JSONNode
        {
            static JSONNull m_StaticInstance = new JSONNull();
            public static bool reuseSameInstance = true;
            public static JSONNull CreateOrGet()
            {
                if (reuseSameInstance)
                    return m_StaticInstance;
                return new JSONNull();
            }
            private JSONNull() { }

            public override JSONNodeType Tag { get { return JSONNodeType.NullValue; } }
            public override bool IsNull { get { return true; } }
            public override Enumerator GetEnumerator() { return new Enumerator(); }

            public override string Value
            {
                get { return "null"; }
                set { }
            }
            public override bool AsBool
            {
                get { return false; }
                set { }
            }

            public override bool Equals(object obj)
            {
                if (object.ReferenceEquals(this, obj))
                    return true;
                return (obj is JSONNull);
            }
            public override int GetHashCode()
            {
                return 0;
            }

            internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
            {
                aSB.Append("null");
            }
        }
        // End of JSONNull

        internal partial class JSONLazyCreator : JSONNode
        {
            private JSONNode m_Node = null;
            private string m_Key = null;
            public override JSONNodeType Tag { get { return JSONNodeType.None; } }
            public override Enumerator GetEnumerator() { return new Enumerator(); }

            public JSONLazyCreator(JSONNode aNode)
            {
                m_Node = aNode;
                m_Key = null;
            }

            public JSONLazyCreator(JSONNode aNode, string aKey)
            {
                m_Node = aNode;
                m_Key = aKey;
            }

            private T Set<T>(T aVal) where T : JSONNode
            {
                if (m_Key == null)
                    m_Node.Add(aVal);
                else
                    m_Node.Add(m_Key, aVal);
                m_Node = null; // Be GC friendly.
                return aVal;
            }

            public override JSONNode this[int aIndex]
            {
                get { return new JSONLazyCreator(this); }
                set { Set(new JSONArray()).Add(value); }
            }

            public override JSONNode this[string aKey]
            {
                get { return new JSONLazyCreator(this, aKey); }
                set { Set(new JSONObject()).Add(aKey, value); }
            }

            public override void Add(JSONNode aItem)
            {
                Set(new JSONArray()).Add(aItem);
            }

            public override void Add(string aKey, JSONNode aItem)
            {
                Set(new JSONObject()).Add(aKey, aItem);
            }

            public static bool operator ==(JSONLazyCreator a, object b)
            {
                if (b == null)
                    return true;
                return System.Object.ReferenceEquals(a, b);
            }

            public static bool operator !=(JSONLazyCreator a, object b)
            {
                return !(a == b);
            }

            public override bool Equals(object obj)
            {
                if (obj == null)
                    return true;
                return System.Object.ReferenceEquals(this, obj);
            }

            public override int GetHashCode()
            {
                return 0;
            }

            public override int AsInt
            {
                get { Set(new JSONNumber(0)); return 0; }
                set { Set(new JSONNumber(value)); }
            }

            public override float AsFloat
            {
                get { Set(new JSONNumber(0.0f)); return 0.0f; }
                set { Set(new JSONNumber(value)); }
            }

            public override double AsDouble
            {
                get { Set(new JSONNumber(0.0)); return 0.0; }
                set { Set(new JSONNumber(value)); }
            }

            public override long AsLong
            {
                get
                {
                    if (longAsString)
                        Set(new JSONString("0"));
                    else
                        Set(new JSONNumber(0.0));
                    return 0L;
                }
                set
                {
                    if (longAsString)
                        Set(new JSONString(value.ToString()));
                    else
                        Set(new JSONNumber(value));
                }
            }

            public override bool AsBool
            {
                get { Set(new JSONBool(false)); return false; }
                set { Set(new JSONBool(value)); }
            }

            public override JSONArray AsArray
            {
                get { return Set(new JSONArray()); }
            }

            public override JSONObject AsObject
            {
                get { return Set(new JSONObject()); }
            }
            internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode)
            {
                aSB.Append("null");
            }
        }
        // End of JSONLazyCreator

        public static class JSON
        {
            public static JSONNode Parse(string aJSON)
            {
                return JSONNode.Parse(aJSON);
            }
        }
    }
}
